package io.swagger.codegen.languages;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.swagger.codegen.AbstractGenerator;
import io.swagger.codegen.CliOption;
import io.swagger.codegen.CodegenConstants;
import io.swagger.codegen.CodegenModel;
import io.swagger.codegen.CodegenOperation;
import io.swagger.codegen.CodegenParameter;
import io.swagger.codegen.CodegenProperty;
import io.swagger.codegen.CodegenResponse;
import io.swagger.codegen.CodegenSecurity;
import io.swagger.codegen.CodegenType;
import io.swagger.codegen.SupportingFile;
import io.swagger.models.Operation;
import io.swagger.models.Path;
import io.swagger.models.Swagger;
import io.swagger.models.properties.ArrayProperty;
import io.swagger.models.properties.ObjectProperty;
import io.swagger.models.properties.Property;
import io.swagger.models.properties.RefProperty;

public class WecloudSpringMvcCodegen extends AbstractJavaCodegen {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(AbstractGenerator.class);
    
    public static final String TITLE = "title";
    public static final String CONFIG_PACKAGE = "configPackage";
    public static final String BASE_PACKAGE = "basePackage";
    public static final String INTERFACE_ONLY = "interfaceOnly";
    public static final String SINGLE_CONTENT_TYPES = "singleContentTypes";
    public static final String JAVA_8 = "java8";
    public static final String ASYNC = "async";
    public static final String SPRING_MVC_LIBRARY = "spring-mvc";
    public static final String SPRING_CLOUD_LIBRARY = "spring-cloud";
    public static final String SPRING_BOOT_LIBRARY = "spring-boot";
    
	public static final String DEFAULT_LIBRARY = SPRING_MVC_LIBRARY;
    
    // add
//    public static final String RESPONSE_RESULT_CLASS_NAME = "ResponseResult";
    public static final String MODEL_MODUELE_ARTIFACT_ID = "modelModuleArtifactId";
    public static final String API_MODUELE_ARTIFACT_ID = "apiModuleArtifactId";
    
//    public static final String COMPONENT_DATA_CLASS_NAME = "ComponentData";
//    public static final String COMPONENT_RETURN_BASE_TYPE = "ComponentData<?>";
    
    // add
    public static final String API_TEMPLATE_FILE_NAME = "api.mustache";
    public static final String CONTROLLER_TEMPLATE_FILE_NAME = "apiController.mustache";
    public static final String SERVICE_TEMPLATE_FILE_NAME = "apiService.mustache";
    
//    public static final String MODULES_DIR = "modules/";
    public static final String MODULES_DIR = "";

    protected String title = "wecloud-swagger-spring-mvc";
    protected String basePackage = "com.wisedu.swagger";
    protected String configPackage = basePackage + ".configuration";
    protected String servicePackage = basePackage + ".service";
    protected boolean interfaceOnly = false;
    protected boolean singleContentTypes = false;
    protected boolean java8 = false;
    protected boolean async = false;
    
    protected Set<String> componentTags = new HashSet<String>();
    
	// add
    protected String modelModuleArtifactId = "swagger-model-module";
	protected String apiModuleArtifactId = "swagger-api-module";

    public WecloudSpringMvcCodegen() {
        super();
        outputFolder = "generated-code/javaSpring-Wecloud";
        apiTestTemplateFiles.clear(); // TODO: add test template
        embeddedTemplateDir = templateDir = "JavaSpring-Wecloud";
        apiPackage = basePackage + ".api";
        controllerPackage = basePackage + ".controller";
        servicePackage = basePackage + ".service";
        modelPackage = basePackage + ".model";
        invokerPackage = basePackage + ".api";
        artifactId = "swagger-spring";

        additionalProperties.put(CONFIG_PACKAGE, configPackage);
        additionalProperties.put(BASE_PACKAGE, basePackage);
        setSerializableModel(Boolean.TRUE);

        cliOptions.add(new CliOption(TITLE, "server title name or client service name"));
        cliOptions.add(new CliOption(CONFIG_PACKAGE, "configuration package for generated code"));
        cliOptions.add(new CliOption(BASE_PACKAGE, "base package for generated code"));
        cliOptions.add(CliOption.newBoolean(INTERFACE_ONLY, "Whether to generate only API interface stubs without the server files."));
        cliOptions.add(CliOption.newBoolean(SINGLE_CONTENT_TYPES, "Whether to select only one produces/consumes content-type by operation."));
        cliOptions.add(CliOption.newBoolean(JAVA_8, "use java8 default interface"));
        cliOptions.add(CliOption.newBoolean(ASYNC, "use async Callable controllers"));

        supportedLibraries.put(DEFAULT_LIBRARY, "Spring-boot Server application using the SpringFox integration.");
        supportedLibraries.put(SPRING_MVC_LIBRARY, "Spring-MVC Server application using the SpringFox integration.");
        supportedLibraries.put(SPRING_CLOUD_LIBRARY, "Spring-Cloud-Feign client with Spring-Boot auto-configured settings.");
        setLibrary(DEFAULT_LIBRARY);

        CliOption library = new CliOption(CodegenConstants.LIBRARY, "library template (sub-template) to use");
        library.setDefault(DEFAULT_LIBRARY);
        library.setEnum(supportedLibraries);
        library.setDefault(DEFAULT_LIBRARY);
        cliOptions.add(library);
        
        setDateLibrary("legacy");
    }

    @Override
    public CodegenType getTag() {
        return CodegenType.SERVER;
    }

    @Override
    public String getName() {
        return "spring-mvc-wecloud";
    }

    @Override
    public String getHelp() {
        return "Generates a Java Webcloud-SpringMvc Server application using the SpringFox integration.";
    }

    @Override
    public void processOpts() {
        super.processOpts();

        // clear model and api doc template as this codegen
        // does not support auto-generated markdown doc at the moment
        //TODO: add doc templates
        modelDocTemplateFiles.remove("model_doc.mustache");
        apiDocTemplateFiles.remove("api_doc.mustache");

        if (additionalProperties.containsKey(TITLE)) {
            this.setTitle((String) additionalProperties.get(TITLE));
        }

        if (additionalProperties.containsKey(CONFIG_PACKAGE)) {
            this.setConfigPackage((String) additionalProperties.get(CONFIG_PACKAGE));
        }

        if (additionalProperties.containsKey(BASE_PACKAGE)) {
            this.setBasePackage((String) additionalProperties.get(BASE_PACKAGE));
        }

        if (additionalProperties.containsKey(INTERFACE_ONLY)) {
            this.setInterfaceOnly(Boolean.valueOf(additionalProperties.get(INTERFACE_ONLY).toString()));
        }

        if (additionalProperties.containsKey(SINGLE_CONTENT_TYPES)) {
            this.setSingleContentTypes(Boolean.valueOf(additionalProperties.get(SINGLE_CONTENT_TYPES).toString()));
        }

        if (additionalProperties.containsKey(JAVA_8)) {
            this.setJava8(Boolean.valueOf(additionalProperties.get(JAVA_8).toString()));
        }

        if (additionalProperties.containsKey(ASYNC)) {
            this.setAsync(Boolean.valueOf(additionalProperties.get(ASYNC).toString()));
        }
        
        if (additionalProperties.containsKey(MODEL_MODUELE_ARTIFACT_ID)) {
            this.setModelModuleArtifactId(additionalProperties.get(MODEL_MODUELE_ARTIFACT_ID).toString());
        }
        
        if (additionalProperties.containsKey(API_MODUELE_ARTIFACT_ID)) {
            this.setApiModuleArtifactId(additionalProperties.get(API_MODUELE_ARTIFACT_ID).toString());
        }
        
        if (additionalProperties.containsKey("componentTags")) {
        	String[] tags = additionalProperties.get("componentTags").toString().split("[|]");
        	if (tags != null && tags.length > 0) {
        		Set<String> tagSet = new HashSet<String>();
        		for (String tag: tags) {
        			if (tag != null && !tag.isEmpty()) {
        				tagSet.add(tag.trim().toLowerCase());
        			}
        		}
        		this.setComponentTags(tagSet);
        	}
        }

        supportingFiles.add(new SupportingFile("pom-parent.mustache", "", "pom.xml"));
        supportingFiles.add(new SupportingFile("pom-api.mustache", apiProjectFolder(), "pom.xml"));
        supportingFiles.add(new SupportingFile("pom-model.mustache", modelProjectFolder(), "pom.xml"));
//        supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));

        if (!this.interfaceOnly) {
            if (library.equals(SPRING_BOOT_LIBRARY)) {
                supportingFiles.add(new SupportingFile("homeController.mustache",
                        (sourceFolder + File.separator + configPackage).replace(".", java.io.File.separator), "HomeController.java"));
                supportingFiles.add(new SupportingFile("swagger2SpringBoot.mustache",
                        (sourceFolder + File.separator + basePackage).replace(".", java.io.File.separator), "Swagger2SpringBoot.java"));
                supportingFiles.add(new SupportingFile("application.mustache",
                        ("src.main.resources").replace(".", java.io.File.separator), "application.properties"));
            }
            
            if (library.equals(SPRING_MVC_LIBRARY)) {
//                supportingFiles.add(new SupportingFile("webApplication.mustache",
//                        (sourceFolder + File.separator + configPackage).replace(".", java.io.File.separator), "WebApplication.java"));
//                supportingFiles.add(new SupportingFile("webMvcConfiguration.mustache",
//                        (sourceFolder + File.separator + configPackage).replace(".", java.io.File.separator), "WebMvcConfiguration.java"));
//                supportingFiles.add(new SupportingFile("swaggerUiConfiguration.mustache",
//                        (sourceFolder + File.separator + configPackage).replace(".", java.io.File.separator), "SwaggerUiConfiguration.java"));
//                supportingFiles.add(new SupportingFile("application.properties",
//                        ("src.main.resources").replace(".", java.io.File.separator), "swagger.properties"));
            }
            
            if (library.equals(SPRING_CLOUD_LIBRARY)) {
                supportingFiles.add(new SupportingFile("apiKeyRequestInterceptor.mustache",
                        (sourceFolder + File.separator + configPackage).replace(".", java.io.File.separator), "ApiKeyRequestInterceptor.java"));
                supportingFiles.add(new SupportingFile("clientConfiguration.mustache",
                        (sourceFolder + File.separator + configPackage).replace(".", java.io.File.separator), "ClientConfiguration.java"));
                
                apiTemplateFiles.put("apiClient.mustache", "Client.java");
                if (!additionalProperties.containsKey(SINGLE_CONTENT_TYPES)) {
                    additionalProperties.put(SINGLE_CONTENT_TYPES, "true");
                    this.setSingleContentTypes(true);
                }
                
            } else {
            	apiTemplateFiles.put(CONTROLLER_TEMPLATE_FILE_NAME, "");
            	apiTemplateFiles.put(SERVICE_TEMPLATE_FILE_NAME, "");
//                controllerTemplateFiles.put("apiController.mustache", ".java");
//                supportingFiles.add(new SupportingFile("apiException.mustache",
//                        (sourceFolder + File.separator + apiPackage).replace(".", java.io.File.separator), "ApiException.java"));
//                supportingFiles.add(new SupportingFile("apiResponseMessage.mustache",
//                        (sourceFolder + File.separator + apiPackage).replace(".", java.io.File.separator), "ApiResponseMessage.java"));
//                supportingFiles.add(new SupportingFile("notFoundException.mustache",
//                        (sourceFolder + File.separator + apiPackage).replace(".", java.io.File.separator), "NotFoundException.java"));
//                supportingFiles.add(new SupportingFile("apiOriginFilter.mustache",
//                        (sourceFolder + File.separator + apiPackage).replace(".", java.io.File.separator), "ApiOriginFilter.java"));
//                supportingFiles.add(new SupportingFile("swaggerDocumentationConfig.mustache",
//                        (sourceFolder + File.separator + configPackage).replace(".", java.io.File.separator), "SwaggerDocumentationConfig.java"));
            }
        }
        
        // 生成通用的返回值模型实体
        additionalProperties.put("wis-supportFilePackage", basePackage() + ".support");
        additionalProperties.put("wis-responseResultQualifiedName", "com.wisedu.wecloud.commons.model.ResponseResult");
        additionalProperties.put("wis-componentDataQualifiedName", "com.wisedu.wecloud.commons.model.ComponentData");
        
        
        // 用于pom.xml文件取值
        additionalProperties.put("basePackage", basePackage());
        additionalProperties.put("modelModuleArtifactId", modelModuleArtifactId());
        additionalProperties.put("apiModuleArtifactId", apiModuleArtifactId());
        
        // 公共model由统一Jar包依赖提供，不再单独生成
//        supportingFiles.add(new SupportingFile("ResponseResult.mustache", apiSupportFileRelativeFolder(), RESPONSE_RESULT_CLASS_NAME + ".java"));
//        supportingFiles.add(new SupportingFile("ComponentData.mustache", apiSupportFileRelativeFolder(), COMPONENT_DATA_CLASS_NAME + ".java"));

        if (this.java8) {
            additionalProperties.put("javaVersion", "1.8");
            typeMapping.put("date", "LocalDate");
            typeMapping.put("DateTime", "OffsetDateTime");
            importMapping.put("LocalDate", "java.time.LocalDate");
            importMapping.put("OffsetDateTime", "java.time.OffsetDateTime");
        }
        
        importMapping.put("JsonFormat", "com.fasterxml.jackson.annotation.JsonFormat");
    }

    @Override
    public void addOperationToGroup(String tag, String resourcePath, Operation operation, CodegenOperation co, Map<String, List<CodegenOperation>> operations) {
        String basePath = resourcePath;
        if (basePath.startsWith("/")) {
            basePath = basePath.substring(1);
        }
        int pos = basePath.indexOf("/");
        if (pos > 0) {
            basePath = basePath.substring(0, pos);
        }

        if (basePath == "") {
            basePath = "default";
        } else {
            co.subresourceOperation = !co.path.isEmpty();
        }
        List<CodegenOperation> opList = operations.get(basePath);
        if (opList == null) {
            opList = new ArrayList<CodegenOperation>();
            operations.put(basePath, opList);
        }
        opList.add(co);
        co.baseName = basePath;
    }

    @Override
    public void preprocessSwagger(Swagger swagger) {
        super.preprocessSwagger(swagger);
        if ("/".equals(swagger.getBasePath())) {
            swagger.setBasePath("");
        }

        if(!additionalProperties.containsKey(TITLE)) {
            // From the title, compute a reasonable name for the package and the API
            String title = swagger.getInfo().getTitle();

            // Drop any API suffix
            if (title != null) {
                title = title.trim().replace(" ", "-");
                if (title.toUpperCase().endsWith("API")) {
                    title = title.substring(0, title.length() - 3);
                }

                this.title = camelize(sanitizeName(title), true);
            }
            additionalProperties.put(TITLE, this.title);
        }

        String host = swagger.getHost();
        String port = "8080";
        if (host != null) {
            String[] parts = host.split(":");
            if (parts.length > 1) {
                port = parts[1];
            }
        }

        this.additionalProperties.put("serverPort", port);
        if (swagger != null && swagger.getPaths() != null) {
            for (String pathname : swagger.getPaths().keySet()) {
                Path path = swagger.getPath(pathname);
                if (path.getOperations() != null) {
                    for (Operation operation : path.getOperations()) {
                        if (operation.getTags() != null) {
                            List<Map<String, String>> tags = new ArrayList<Map<String, String>>();
                            for (String tag : operation.getTags()) {
                                Map<String, String> value = new HashMap<String, String>();
                                value.put("tag", tag);
                                value.put("hasMoreTag", "true");
                                tags.add(value);
                            }
                            if (tags.size() > 0) {
                                tags.get(tags.size() - 1).remove("hasMoreTag");
                            }
                            operation.setVendorExtension("x-tags", tags);
                            // tags ==> tag1 | tag2 | ...
                            if (operation.getTags().size() > 0) {
                            	StringBuilder tagsBuilder = new StringBuilder(operation.getTags().get(0));
                            	for (int i = 1; i < operation.getTags().size(); ++i) {
                            		tagsBuilder.append("|").append(operation.getTags().get(i));
                            	}
                                operation.setTags(Arrays.asList(tagsBuilder.toString()));
                            }
                        }
                    }
                }
            }
        }
    }

    private boolean containsComponentsTag(List<String> tags) {
		if (tags == null || tags.isEmpty() || getComponentTags() == null || getComponentTags().isEmpty()) {
			return false;
		}
		List<String> splitTags = new ArrayList<String>();
		for (String tag: tags) {
			if (tag == null || tag.isEmpty()) {
				continue;
			}
			String[] tagArray = tag.split("[|]");
			if (tagArray != null && tagArray.length > 0) {
				for (String splitTag: tagArray) {
					if (splitTag != null && !splitTag.trim().isEmpty()) {
						splitTags.add(splitTag.trim().toLowerCase());
					}
				}
			}
		}
		for (String tag: splitTags) {
			if (getComponentTags().contains(tag)) {
				return true;
			}
		}
		return false;
	}

	@SuppressWarnings("unchecked")
	@Override
    public Map<String, Object> postProcessSupportingFileData(Map<String, Object> objs) {
        if(library.equals(SPRING_CLOUD_LIBRARY)) {
            List<CodegenSecurity> authMethods = (List<CodegenSecurity>) objs.get("authMethods");
            if (authMethods != null) {
                for (CodegenSecurity authMethod : authMethods) {
                    authMethod.name = camelize(sanitizeName(authMethod.name), true);
                }
            }
        }
        return objs;
    }
    
    @Override
    public boolean shouldOverwrite(String filename) {
    	File file = new File(filename);
    	if (file.exists() && "pom.xml".equals(file.getName())) {
    		return false;
    	} else {
    		return super.shouldOverwrite(filename);
    	}
    }

    @Override
    public String toApiName(String name) {
        if (name.length() == 0) {
            return "DefaultApi";
        }
        name = sanitizeName(name);
        return camelize(name) + "Api";
    }
    
    /**
     * 覆盖默认的zpi文件路径，添加子工程路径前缀
     * @see io.swagger.codegen.DefaultCodegen#apiFilename(java.lang.String, java.lang.String)
     */
    @Override
	public String apiFilename(String templateName, String tag) {
    	if (API_TEMPLATE_FILE_NAME.equals(templateName)) {
    		return apiFilePathName(tag);
    	} else if (CONTROLLER_TEMPLATE_FILE_NAME.equals(templateName)) {
    		return controllerFilePathName(tag);
    	} else if (SERVICE_TEMPLATE_FILE_NAME.equals(templateName)) {
    		return serviceFilePathName(tag);
    	} else {
    		return apiFilePathName(tag);
    	}
	}
    
    public Set<String> getComponentTags() {
		return componentTags;
	}

	public void setComponentTags(Set<String> componentTags) {
		this.componentTags = componentTags;
	}
    
    /** 
	 * 覆盖默认的model文件路径，添加子工程路径前缀
     * @see io.swagger.codegen.languages.AbstractJavaCodegen#modelFileFolder()
     */
    @Override
	public String modelFileFolder() {
    	return outputFolder() + "/" + modelProjectFolder() + "/" + sourceFolder + "/" + modelPackage().replace('.', '/');
	}

	@Override
	public String apiFileFolder() {
		return outputFolder() + "/" + apiProjectFolder() + "/" + sourceFolder + "/" + apiPackage().replace('.', '/');
	}
	
	public String controllerFileFolder() {
		return outputFolder() + "/" + apiProjectFolder() + "/" + sourceFolder + "/" + controllerPackage().replace('.', '/');
	}
	
	public String serviceFileFolder() {
		return outputFolder() + "/" + apiProjectFolder() + "/" + sourceFolder + "/" + servicePackage().replace('.', '/');
	}
	
	public String apiSupportFileRelativeFolder() {
		return apiProjectFolder() + "/" + sourceFolder + "/" + basePackage().replace('.', '/') + "/support";
	}

	private String apiProjectFolder() {
    	return MODULES_DIR + apiModuleArtifactId();
    }
	
	private String modelProjectFolder() {
    	return MODULES_DIR + modelModuleArtifactId();
    }

	public String toControllerName(String name) {
        if (name.length() == 0) {
            return "DefaultController";
        }
        name = sanitizeName(name);
        return camelize(name) + "Controller";
    }
	
	private String toInterfaceImport(String tag) {
		return apiPackage() + "." + toApiName(tag);
	}
    
    public String toControllerImport(String name) {
        return controllerPackage() + "." + toControllerName(name);
    }
    
    public String toServiceImport(String name) {
    	return servicePackage() + "." + toServiceName(name);
    }
    
    public String basePackage() {
		return basePackage;
	}
    
    public String apiPackage() {
        return apiPackage;
    }
    
    public String controllerPackage() {
        return controllerPackage;
    }
    
    public String servicePackage() {
        return servicePackage;
    }
    
    public String modelPackage() {
        return modelPackage;
    }
    
    public String toServiceName(String name) {
        if (name.length() == 0) {
            return "DefaultService";
        }
        name = sanitizeName(name);
        return camelize(name) + "Service";
    }
    
    public String controllerFileName(String name) {
    	return toControllerName(name) + ".java";
    }
    
    public String serviceFileName(String name) {
    	return toServiceName(name) + ".java";
    }
    
    public String apiFilePathName(String name) {
    	return apiFileFolder() + "/" + toApiName(name) + ".java";
    }
    
    public String controllerFilePathName(String name) {
    	return controllerFileFolder() + "/" + controllerFileName(name);
    }
    
    public String serviceFilePathName(String name) {
    	return serviceFileFolder() + "/" + serviceFileName(name);
    }
    
    public String controllerTemplateFile() {
    	return "apiController.mustache";
    }
    
    public String serviceTemplateFile() {
    	return "apiService.mustache";
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public void setConfigPackage(String configPackage) {
        this.configPackage = configPackage;
    }

    public void setBasePackage(String configPackage) {
        this.basePackage = configPackage;
        this.apiPackage = this.basePackage + ".api";
        this.controllerPackage = this.basePackage + ".controller";
        this.servicePackage = this.basePackage + ".service";
        this.modelPackage = this.basePackage + ".model";
    }
    
    public String modelModuleArtifactId() {
		return modelModuleArtifactId;
	}

	public void setModelModuleArtifactId(String modelModuleArtifactId) {
		this.modelModuleArtifactId = modelModuleArtifactId;
	}

	public String apiModuleArtifactId() {
		return apiModuleArtifactId;
	}

	public void setApiModuleArtifactId(String apiModuleArtifactId) {
		this.apiModuleArtifactId = apiModuleArtifactId;
	}

    public void setInterfaceOnly(boolean interfaceOnly) { this.interfaceOnly = interfaceOnly; }

    public void setSingleContentTypes(boolean singleContentTypes) {
        this.singleContentTypes = singleContentTypes;
    }

    public void setJava8(boolean java8) { this.java8 = java8; }

    public void setAsync(boolean async) { this.async = async; }

    @Override
	public Map<String, Object> postProcessOperations(Map<String, Object> objs) {
		return objs;
	}

	/* (non-Javadoc)
	 * @see io.swagger.codegen.languages.AbstractJavaCodegen#postProcessModels(java.util.Map)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public Map<String, Object> postProcessModels(Map<String, Object> objs) {
		super.postProcessModels(objs);

        //Add imports for validate
        List<Map<String, String>> imports = (List<Map<String, String>>) objs.get("imports");
        List<Object> models = (List<Object>) objs.get("models");
        for (Object modelObject : models) {
            Map<String, Object> modelMap = (Map<String, Object>) modelObject;
            CodegenModel codegenModel = (CodegenModel) modelMap.get("model");
            
            List<CodegenProperty> properties = codegenModel.vars;
    		if (properties == null || properties.isEmpty()) {
    			continue ;
    		}
    		for (CodegenProperty property: properties) {
    			if (StringUtils.isNotEmpty(property.pattern)) {
    				imports.add(Collections.singletonMap("import", "javax.validation.constraints.*"));
    				imports.add(Collections.singletonMap("import", "org.hibernate.validator.constraints.*"));
    				imports.add(Collections.singletonMap("import", "com.wisedu.commons.validator.constraints.*"));
    			}
    		}
        }

        return objs;
	}

	@Override
    public void postProcessModelProperty(CodegenModel model, CodegenProperty property) {
        super.postProcessModelProperty(model, property);

        if("null".equals(property.example)) {
            property.example = null;
        }

        // Add imports for Jackson
        if(!BooleanUtils.toBoolean(model.isEnum)) {
            model.imports.add("JsonProperty");

            if(BooleanUtils.toBoolean(model.hasEnums)) {
                model.imports.add("JsonValue");
            }
        }
        if (BooleanUtils.toBoolean(property.isDate) || BooleanUtils.toBoolean(property.isDateTime)) {
        	model.imports.add("JsonFormat");
        }
    }

    @SuppressWarnings("unchecked")
	@Override
    public Map<String, Object> postProcessModelsEnum(Map<String, Object> objs) {
        super.postProcessModelsEnum(objs);

        //Add imports for Jackson
        List<Map<String, String>> imports = (List<Map<String, String>>)objs.get("imports");
        List<Object> models = (List<Object>) objs.get("models");
        for (Object _mo : models) {
            Map<String, Object> mo = (Map<String, Object>) _mo;
            CodegenModel cm = (CodegenModel) mo.get("model");
            // for enum model
            if (Boolean.TRUE.equals(cm.isEnum) && cm.allowableValues != null) {
                cm.imports.add(importMapping.get("JsonValue"));
                Map<String, String> item = new HashMap<String, String>();
                item.put("import", importMapping.get("JsonValue"));
                imports.add(item);
            }
        }

        return objs;
    }
    
	@Override
	public boolean generateUndeclareModel() {
		return false;
	}
	
	private Map<String, Object> definedModelMap;
	
	/* (non-Javadoc)
	 * @see io.swagger.codegen.DefaultCodegen#postProcessAllModels(java.util.Map)
	 */
	@Override
	public Map<String, Object> postProcessAllModels(Map<String, Object> objs) {
		return definedModelMap = super.postProcessAllModels(objs);
	}

	@SuppressWarnings("unchecked")
	@Override
	public void postProcessPathOperations(String tag, List<CodegenOperation> ops, Map<String, Object> operation) {
		
    	operation.put("wis-interfaceImport", this.toInterfaceImport(tag));
    	operation.put("wis-serviceImport", this.toServiceImport(tag));
    	operation.put("wis-controllerPackage", this.controllerPackage());
    	operation.put("wis-interfaceClassName", this.toApiName(tag));
    	operation.put("wis-controllerClassName", this.toControllerName(tag));
    	operation.put("wis-servicePackage", this.servicePackage());
        operation.put("wis-serviceClassName", this.toServiceName(tag));
        operation.put("wis-serviceClassName", this.toServiceName(tag));
		
		if (ops == null || ops.size() == 0) {
			return ;
		}
		
		List<Map<String, String>> imports = (List<Map<String, String>>) operation.get("imports");
		if (imports == null) {
			imports = new ArrayList<>();
			operation.put("imports", imports);
		}
		
		boolean containsListType = false, containsMultipartFileType = false;
		boolean containsBodyParameter = false;
		boolean containsDateQueryParameter = false;
		for (CodegenOperation codegenOperation: ops) {
			
            processMethodReturnType(imports, codegenOperation, operation);
			
			if (codegenOperation.allParams == null || codegenOperation.allParams.size() == 0) {
				continue;
			}
			for (CodegenParameter parameter: codegenOperation.allParams) {
				if (parameter == null) {
					continue;
				}
				if (parameter.isListContainer != null && parameter.isListContainer.booleanValue()) {
					containsListType = true;
				} else if (parameter.isFile != null && parameter.isFile.booleanValue()) {
					containsMultipartFileType = true;
				}
				// add validate
				if (!containsBodyParameter && BooleanUtils.isTrue(parameter.isBodyParam)) {
					containsBodyParameter = true;
				}
				
				// add dataFormat
				if (BooleanUtils.isTrue(parameter.isQueryParam) && 
						(BooleanUtils.isTrue(parameter.isDate) || BooleanUtils.isTrue(parameter.isDateTime))) {
					containsDateQueryParameter = true;
				}
			}
		}
		if (containsListType) {
			imports.add(Collections.singletonMap("import", "java.util.List"));
		}
		if (containsMultipartFileType) {
			imports.add(Collections.singletonMap("import", "org.springframework.web.multipart.MultipartFile"));
		}
		if (containsBodyParameter) {
			operation.put("wis-containsBodyParameter", true);
			operation.put("wis-containsListType", containsListType);
		}
		operation.put("wis-containsDateQueryParameter", containsDateQueryParameter);
		
		// 去除多余import
		Set<String> importSet = new HashSet<String>();
		for (Iterator<Map<String, String>> iterator = imports.iterator(); iterator.hasNext(); ) {
			Map<String, String> nextImport = iterator.next();
			if (importSet.contains(nextImport.get("import"))) {
				iterator.remove();
			} else {
				importSet.add(nextImport.get("import"));
			}
		}
	}

	private void processMethodReturnType(List<Map<String, String>> imports, CodegenOperation codegenOperation, 
			Map<String, Object> datas) {
		codegenOperation.returnType = "?";
		codegenOperation.returnContainer = null;
		
		boolean containsComponentsTag = containsComponentsTag(codegenOperation.tags);
		if (containsComponentsTag) {
        	datas.put("wis-containsComponentTag", true);
        }
		
		// 处理response中的datas类型 和 componentData中的rows类型
		if (codegenOperation.responses != null && codegenOperation.responses.size() > 0) {
			
			CodegenResponse codegenResponse = codegenOperation.responses.get(0);
			if (!(codegenResponse.schema instanceof ObjectProperty)) {
				return ;
			}
			
			Map<String, Property> properties = ((ObjectProperty) codegenResponse.schema).getProperties();
			if (properties == null || properties.isEmpty() || !properties.containsKey("datas")) {
				return ;
			}
			
			Property datasProperty = properties.get("datas");
			if (datasProperty instanceof ObjectProperty) {
				if (containsComponentsTag) {
					Property rowsProperty = ((ObjectProperty) datasProperty).getProperties().get("rows");
					if (rowsProperty instanceof ArrayProperty) {
						Property items = ((ArrayProperty) rowsProperty).getItems();
						if (items instanceof RefProperty) {
							setMethodResturnTypeByRef((RefProperty) items, codegenOperation, imports, null);
							codegenOperation.returnType = "ComponentData<" + codegenOperation.returnType + ">";
							LOGGER.info("包装组件类型返回值:{}", codegenOperation.returnType);
						}
					} else {
						// ignore
					}
				}
			} else if (datasProperty instanceof RefProperty) {
				setMethodResturnTypeByRef((RefProperty) datasProperty, codegenOperation, imports, null);
			} else if (datasProperty instanceof ArrayProperty) {
				Property items = ((ArrayProperty) datasProperty).getItems();
				if (items instanceof RefProperty) {
					setMethodResturnTypeByRef((RefProperty) items, codegenOperation, imports, "List");
				}
			}
			
		}
	}
	
	@SuppressWarnings("unchecked")
	private void setMethodResturnTypeByRef(RefProperty refProperty, CodegenOperation methodOperation, 
			List<Map<String, String>> imports, String containerType) {
		String refObjectName = refProperty.getSimpleRef();
		Map<String, Object> modelDataMap = (Map<String, Object>) definedModelMap.get(refObjectName);
		if (modelDataMap != null && !modelDataMap.isEmpty() && modelDataMap.containsKey("classname")) {
			String modelClassName = (String) modelDataMap.get("classname");
			imports.add(Collections.singletonMap("import", modelPackage() + "." + modelClassName));
			if ("List".equals(containerType)) {
				imports.add(Collections.singletonMap("import", "java.util.List"));
				methodOperation.returnType = "List<" + modelClassName + ">";
			} else if ("Set".equals(containerType)) {
				imports.add(Collections.singletonMap("import", "java.util.Set"));
				methodOperation.returnType = "Set<" + modelClassName + ">";
			} else {
				methodOperation.returnType = modelClassName;
			}
		} else {
			methodOperation.returnType = "?";
		}
	}
	
	@Override
	public String sanitizeTag(String tag) {
		return tag;
	}

}
